#include <iostream>
#include "./Include/CConfigIni.h"
#include "./Include/global.h"
#include "./Include/my_exception.h"
#include "boost/asio.hpp"
#include <thread>
#include "my_thread.h"
#include "./Include/global.h"
#include <syslog.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>

using namespace std;
namespace asios = boost::asio;

//调用boost.asio库创建一个tcp服务器，功能包括：
//1.支持最少500个并发连接,响应延时低于100ms
//2.后台进程，可通过控制台输入指令控制
//3.使用boost.log将日志记录到syslog中
//4.返回客户端发送的数据
//5.用syslog写日志

//当前已经连接的线程总数
std::atomic<int> curThreadNum(0);

//创建守护进程
void DaemonProcess()
{
    /*
    守护进程要点：
    1、屏蔽一些有关控制终端操作的信号，是为了防止在守护进程没有正常启动起来前，控制终端受到干扰退出或挂起。
    2、在后台运行
    3、脱离控制终端和进程组：
        (1)一个进程属于一个进程组，进程组号(PGID)就是进程组长的进程号(PID)
        (2)同进程组中的进程共享一个控制终端，这个控制终端默认是创建进程的终端
        (3)一个进程关联的控制终端和进程组通常是从父进程继承下来的，因此，这个子进程仍然受到父亲进程终端的影响，因为终端产生的信号会发送给前台进程组的所有进程。
    4、禁止进程重新打开控制终端，采用的办法是再次创建一个子进程，并让父亲进程退出，该子进程不再是会话组长，从而达到目的。
    5、关闭打开的文件描述符。因为进程从创建它的父进程那里继承了打开的文件描述符，一般情况下不再需要。如不关闭，将会浪费系统资源。
    6、改变当前工作目录。进程活动时，其工作目录所在的文件系统不能卸载。因此需要将守护进程的工作目录改变到合适的目录。
    7、重设文件创建掩码。进程从创建它的父进程那里继承了文件创建掩码。它可能修改守护进程所创建的文件的存取权限。
    8、处理SIGCHLD信号(子进程退出信号)。如果不等待子进程结束，子进程将成为僵尸进程从而占用系统内核资源。
    */

    //setp1:屏蔽终端信号，防止受到终端干扰而退出
    signal(SIGTTOU,SIG_IGN);
    signal(SIGTTIN,SIG_IGN);
    signal(SIGTSTP,SIG_IGN);
    signal(SIGHUP,SIG_IGN);

    //step2:脱离终端，后台运行
    int pid = fork();
    if(pid > 0)
        exit(0);        //父进程退出，子进程继续
    else if(pid < 0)
        return;         //异常进程，退出

    //step3：脱离原有进程组、会话和控制终端，进程变成进程组长
    setsid();

    //step4：重新创建子进程，防止重新打开终端，此时，进程已经不是进程组长了，不能打开控制终端
    pid =fork();
    if(pid > 0)
        exit(0);
    else if(pid < 0)
        return ;

    //step5：关闭文件描述符
    for(int i =0; i< 256;i++)
        close(i);

    //step6:改变工作目录，防止当前文件系统被卸载
    chdir("/tmp/socket_server");

    //step7:重设文件掩码
    umask(0);

    //将输入输出重定向
    int fd0,fd1,fd2;
    fd0 = open("/dev/null",O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);

    //step8:讲子进程退出信号设置为SIG_IGN，让系统帮助回收进程资源
    signal(SIGCHLD,SIG_IGN);
}

int main(int argc, char** argv)
{
    std::string msg;
    char tempChar[128] = {0x00};

    //创建守护进程
    DaemonProcess();

    //调用syslog记录日志信息
    openlog("reflexserver",LOG_CONS|LOG_PID,0);
    syslog(LOG_INFO,"reflex server 启动成功");

    //加载配置文件
    std::string iniPath = CConfigIni::APP_PATH+"/config.ini";    
    if(!G_INIFILE->LoadConfigIni(iniPath))
    {        
        msg = "配置文件加载失败，"+G_INIFILE->GetErrMsg();
        syslog(LOG_ERR,msg.c_str());
        closelog();
        return -1;
    }

    //启动服务器（同步、阻塞服务器）
    syslog(LOG_INFO,"启动服务器");
    asios::io_service sev_io_server;
    asios::ip::tcp::acceptor acceptors(sev_io_server,
        asios::ip::tcp::endpoint(asios::ip::tcp::v4(),
        G_INIFILE->GetServerPort()));
    while(true)
    {
        //存储客户端的连接句柄
        asios::ip::tcp::socket* pSock = new asios::ip::tcp::socket(sev_io_server);                       
        acceptors.accept(*pSock);    //等待客户端到来

        if(curThreadNum < G_INIFILE->GetMaxConnection())
        //if(true)
        {
            curThreadNum++;            
            //创建线程
            thread t(TaskThread,pSock);
            t.detach();
        }
        else
        {
            string clientIP = pSock->remote_endpoint().address().to_string();
            unsigned int clientPort = pSock->remote_endpoint().port();
            boost::format fmt("客户端连接响应成功但连接数超过最大限制，强制关闭连接");      //格式化字符串
            syslog(LOG_ERR,fmt.str().c_str());            
        }            
    }
    
    //关闭日志接口
    closelog();
    return 0;
}